﻿using Colorful.Caching;
using Colorful.Furniture.CommonDto;
using Colorful.Furniture.EnterpriseManagement;
using Colorful.Furniture.Extensions;
using Colorful.Furniture.MpManagement;
using Colorful.Furniture.QrCode;
using Colorful.Furniture.UserManagement;
using Colorful.Furniture.Wrappers;
using FreeSql;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using NETCore.Encrypt;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Security.Claims;

namespace Colorful.Furniture.PermissionManagement
{
    /// <summary>
    ///  管理员服务
    /// </summary>
    [Authorize]
    public class ManagerAppService : ApplicationService
    {
        private readonly IFreeSql _freeSql;
        private readonly IBaseRepository<SysManager> _managerRepository;
        private readonly IBaseRepository<SysManagerIdentity> _sysManagerIdentity;
        private readonly IBaseRepository<SysManagerRole> _sysManagerRoleRepository;
        private readonly IBaseRepository<Department> _sysDepartmentRepository;
        private readonly ColorfulJwtOptions _jwtConfig;
        private readonly IRedisCache _cache;
        private readonly VerifyCodeAppService _verifyCodeAppService;
        private readonly QrCodeAppService _qrCodeAppService;
        private readonly WechatMpOptions _wechatMpOptions;
        private readonly WechatMpUserAppService _mpUserAppService;
        private readonly ILogger<ManagerAppService> _logger;

        /// <summary>
        ///  管理员服务
        /// </summary>
        public ManagerAppService(IFreeSql freeSql,
            IBaseRepository<SysManager> managerRepository,
            IBaseRepository<SysManagerIdentity> sysManagerIdentity,
            IBaseRepository<SysManagerRole> sysManagerRoleRepository,
            IRedisCache cache,
            VerifyCodeAppService verifyCodeAppService,
            IOptions<ColorfulJwtOptions> jwtConfig,
            QrCodeAppService qrCodeAppService,
            IOptions<WechatMpOptions> wechatMpOptions,
            WechatMpUserAppService mpUserAppService,
            ILogger<ManagerAppService> logger,
            IBaseRepository<Department> sysDepartmentRepository)
        {
            _freeSql = freeSql;
            _managerRepository = managerRepository;
            _sysManagerIdentity = sysManagerIdentity;
            _sysManagerRoleRepository = sysManagerRoleRepository;
            _cache = cache;
            _verifyCodeAppService = verifyCodeAppService;
            _qrCodeAppService = qrCodeAppService;
            _mpUserAppService = mpUserAppService;
            _logger = logger;
            _sysDepartmentRepository = sysDepartmentRepository;
            _wechatMpOptions = wechatMpOptions.Value;
            _jwtConfig = jwtConfig.Value;
        }

        /// <summary>
        ///  帐号密码登录
        /// </summary>
        /// <returns></returns>
        [RemoteService(IsEnabled = false)]
        private async Task<ManagerLoginOutputDto> AccountPasswordLoginAsync(LoginInputDto input)
        {
            //获取管理员信息
            var managerDto = await _freeSql.Select<SysManager, SysManagerIdentity, Department>()
                .InnerJoin((a, b, c) => a.Id == b.ManagerId)
                .LeftJoin((a, b, c) => a.DepartmentId == c.Id)
                .Where((a, b, c) => b.Identifier == input.UserName
                                 && b.Credential == input.Password.Encrypt32()
                                 && b.IdentityType == IdentityType.用户名密码
                                 && !a.IsDeleted)
                .ToOneAsync((a, b, c) => new ManagerLoginOutputDto
                {
                    Id = a.Id,
                    DepartmentId = c.Id,
                    DepartmentCode = c.DepartId.ToString(),
                    DepartmentClassId = c.ClassId,
                    DepartmentName = c.Name
                });

            return managerDto;
        }

        /// <summary>
        ///  登录
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        public async Task<string> LoginAsync(LoginInputDto input)
        {
            if (!_verifyCodeAppService.CheckCode(input.VerifyCode, input.VerifyCodeId))
                throw new UserFriendlyException("验证码输入错误", "105");

            //获取登录者信息
            var managerOutput = await AccountPasswordLoginAsync(input);

            if (managerOutput == null)
                throw new UserFriendlyException("用户名不存在或密码不正确", "105");

            // 😊写入相应的claim信息到请求安全体中
            var userClaims = new List<Claim>
            {
                new(AbpClaimTypes.Name,managerOutput.Name),
                new(AbpClaimTypes.UserId ,managerOutput.Id.ToString()),
                new (AbpClaimTypes.UserName,managerOutput.UserName)
            };


            //管理员信息存放到Redis中
            var managerInfo = ObjectMapper.Map<ManagerLoginOutputDto, ManagerInfoOutputDto>(managerOutput);
            await _cache.SetAsync($"{CachePrefixConst.ManagerInfo}:{managerOutput.Id}", managerInfo, _jwtConfig.Expiration * 60);


            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.SecurityKey));
            //☺证书
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var securityToken = new JwtSecurityToken(
                 _jwtConfig.Issuer,
                _jwtConfig.Audience,
                userClaims,
                DateTime.Now,
                 DateTime.Now.AddHours(_jwtConfig.Expiration),
                 credentials);

            var token = new JwtSecurityTokenHandler().WriteToken(securityToken);

            return token;
        }

#if DEBUG

        /// <summary>
        ///  通过用户名直接登录
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [AllowAnonymous]
        public async Task<string> LoginUserNameAsync(string userName)
        {
            //获取管理员信息
            var managerOutput = await _freeSql.Select<SysManager, SysManagerIdentity, Department>()
                .InnerJoin((a, b, c) => a.Id == b.ManagerId)
                .LeftJoin((a, b, c) => a.DepartmentId == c.Id)
                .Where((a, b, c) => b.Identifier == userName
                                    && !a.IsDeleted)
                .ToOneAsync((a, b, c) => new ManagerLoginOutputDto
                {
                    Id = a.Id,
                    DepartmentCode = c.DepartId.ToString(),
                    DepartmentName = c.Name
                });

            if (managerOutput == null)
                throw new UserFriendlyException("用户名不存在或密码不正确", "105");

            // 😊写入相应的claim信息到请求安全体中
            var userClaims = new List<Claim>
            {
                new(AbpClaimTypes.Name,managerOutput.Name),
                new(AbpClaimTypes.UserId ,managerOutput.Id.ToString()),
                new (AbpClaimTypes.UserName,managerOutput.UserName)
            };


            //管理员信息存放到Redis中
            var managerInfo = ObjectMapper.Map<ManagerLoginOutputDto, ManagerInfoOutputDto>(managerOutput);
            await _cache.SetAsync($"{CachePrefixConst.ManagerInfo}:{managerOutput.Id}", managerInfo, _jwtConfig.Expiration * 60);


            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.SecurityKey));
            //☺证书
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var securityToken = new JwtSecurityToken(
                _jwtConfig.Issuer,
                _jwtConfig.Audience,
                userClaims,
                DateTime.Now,
                DateTime.Now.AddHours(_jwtConfig.Expiration),
                credentials);

            var token = new JwtSecurityTokenHandler().WriteToken(securityToken);

            return token;
        }

#endif

        /// <summary>
        ///  获取管理员信息
        /// </summary>
        /// <returns></returns>
        [Authorize]
        [HttpGet]
        public async Task<ManagerInfoOutputDto> InfoAsync()
        {
            //获取管理员的菜单缓存 
            var managerOutput = await _cache.GetAsync<ManagerInfoOutputDto>($"{CachePrefixConst.ManagerInfo}:{CurrentUser.Id}");

            if (managerOutput == null)
                throw new UserFriendlyException("请先进行认证操作", "401");

            //获取角色菜单和权限
            managerOutput.PermissionList = await GetPermissionListAsync(managerOutput.Id);
            managerOutput.MenuList = await GetMenuListAsync(managerOutput.Id);
            managerOutput.RoleList = await GetRoleIdListAsync(managerOutput.Id);

            return managerOutput;
        }

        /// <summary>
        ///  获取管理员的角色列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<Guid?>> GetRoleIdListAsync(Guid managerId)
        {
            return await _sysManagerRoleRepository.Where(x => x.ManagerId == managerId)
                .ToListAsync(x => x.RoleId);

        }

        /// <summary>
        ///  获取管理员的菜单
        /// </summary>
        /// <returns></returns>
        [RemoteService(IsEnabled = false)]
        public async Task<List<PermissionMenuOutputDto>> GetMenuListAsync(Guid managerId)
        {
            //获取管理员当前角色和相应的权限
            var managerRolePermission = await _freeSql
                .Select<SysManagerRole, SysRoleAccess, SysPermission, SysResource>()
                .InnerJoin((a, b, c, d) => a.RoleId == b.RoleId && b.PermissionId == c.Id && c.ResourceId == d.Id)
                .Where((a, b, c, d) => d.ResourceType == (int)ResourceType.管理端资源 && a.ManagerId == managerId && c.OperationName.ToLower() == "show")
                .ToListAsync((a, b, c, d) => new PermissionMenuOutputDto
                {
                    Name = d.Name
                });

            return managerRolePermission;
        }

        /// <summary>
        ///  获取管理员的权限
        /// </summary>
        /// <returns></returns>
        [RemoteService(IsEnabled = false)]
        public async Task<List<PermissionOutputDto>> GetPermissionListAsync(Guid managerId)
        {
            //获取管理员当前角色和相应的权限
            var managerRolePermission = await _freeSql
                .Select<SysManagerRole, SysRoleAccess, SysPermission, SysResource>()
                .InnerJoin((a, b, c, d) => a.RoleId == b.RoleId && b.PermissionId == c.Id && c.ResourceId == d.Id)
                .Where((a, b, c, d) => a.ManagerId == managerId)
                .ToListAsync((a, b, c, d) => new PermissionOutputDto
                {
                    PermissionName = $"{d.Name}_{c.OperationName}",
                    PermissionTitle = $"{d.Title}_{c.OperationTitle}",
                });

            return managerRolePermission;
        }

        /// <summary>
        ///  获取管理员角色列表
        /// </summary>
        /// <returns></returns>
        [RemoteService(IsEnabled = false)]
        public async Task<List<Guid?>> GetRoleListAsync(Guid? managerId)
        {
            var roleIdList = await _sysManagerRoleRepository.Where(x => x.ManagerId == managerId)
                .ToListAsync(x => x.RoleId);

            return roleIdList;
        }

        /// <summary>
        ///  验证管理员的权限
        /// </summary>
        /// <param name="managerId">管理员编码</param>
        /// <param name="permission">权限点</param>
        /// <returns></returns>
        [RemoteService(IsEnabled = false)]
        public async Task<bool> VerifyPermissionAsync(Guid managerId, string permission)
        {
            return await _freeSql
                .Select<SysManager, SysManagerRole, SysRoleAccess, SysPermissionApi, SysApi>()
                .InnerJoin((a, b, c, d, e) => a.Id == b.ManagerId && b.RoleId == c.RoleId && c.PermissionId == d.PermissionId && d.ApiId == e.Id)
                .Where((a, b, c, d, e) => a.Id == managerId && (e.AreaCode + "." + e.ControllerCode + "." + e.ActionCode).ToLower() == permission)
                .AnyAsync();
        }

        /// <summary>
        ///  管理员列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<List<ManagerPageOutputDto>> ListAsync(ManagerPagedInputDto input)
        {
            var managerOutputList = await _freeSql.Select<SysManager>()
                .WhereIf(!string.IsNullOrWhiteSpace(input.Keywords), r => r.Name.Contains(input.Keywords))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Position), r => r.Position.Contains(input.Position))
                .WhereIf(!string.IsNullOrWhiteSpace(input.RoleName), r => r.RoleName.Contains(input.RoleName))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Sex), r => r.Sex.Contains(input.Sex))
                .WhereIf(input.IsDisabled.HasValue, m => m.IsDisabled == input.IsDisabled)
                .OrderBy(r => r.AddTime)
                .ToListAsync(r => new ManagerPageOutputDto
                {
                    Id = r.Id,
                    Name = r.Name,
                    IsDisabled = r.IsDisabled,
                    Birthday = Convert.ToDateTime(r.Birthday).ToString("yyyy-MM-dd"),
                    AddTime = Convert.ToDateTime(r.AddTime).ToString("yyyy-MM-dd")
                });

            return managerOutputList;
        }

        /// <summary>
        ///  管理员分页
        /// </summary>
        /// <returns></returns>
        public async Task<PageResponseWrapper<List<ManagerPageOutputDto>>> PostPagedAsync(ManagerPagedInputDto input)
        {
            var managerPageOutputList = await _freeSql.Select<SysManager, Department>()
                .LeftJoin((m, s) => s.Id == m.DepartmentId)
                .WhereIf(!string.IsNullOrWhiteSpace(input.Keywords), (m, s) => m.Name.Contains(input.Keywords) || m.UserName.Contains(input.Keywords) || m.Phone.Contains(input.Keywords))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Sex), (m, s) => m.Sex.Contains(input.Sex))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Position), (m, s) => m.Position.Contains(input.Position))
                .WhereIf(!string.IsNullOrWhiteSpace(input.RoleName), (m, s) => m.RoleName.Contains(input.RoleName))
                .WhereIf(input.IsDisabled.HasValue, (m, s) => m.IsDisabled == input.IsDisabled)
                .WhereIf(input.OrganizationId.HasValue, (m, s) => s.Id == input.OrganizationId)
                .Count(out var pageTotalCount)
                .Page(input.PageIndex, input.PageSize)
                .OrderBy((m, s) => m.AddTime)
                .ToListAsync((m, s) => new ManagerPageOutputDto
                {
                    Id = m.Id,
                    Name = m.Name,
                    DepartmentName = s.Name,
                    IsDisabled = m.IsDisabled,
                    Birthday = Convert.ToDateTime(m.Birthday).ToString("yyyy-MM-dd"),
                    AddTime = Convert.ToDateTime(m.AddTime).ToString("yyyy-MM-dd HH:mm")
                });

            return new PageResponseWrapper<List<ManagerPageOutputDto>>
            {
                Total = pageTotalCount,
                Items = managerPageOutputList
            };
        }

        /// <summary>
        ///  逻辑管理员离职信息（员工批量离职）
        /// </summary>
        /// <returns></returns>]
        public async Task UpdateStatusAsync([FromBody] ManagerStatesInputDto input)
        {
            // 离职
            if (input.StateType == 0)
            {
                await _freeSql.Update<SysManager>()
                    .Set(x => x.IsDisabled, true)
                    .Set(x => x.IsDeleted, true)
                    .Where(x => input.Id.Contains(x.Id))
                    .ExecuteAffrowsAsync();
            }
            else // 重新入职启用
            {
                await _freeSql.Update<SysManager>()
                    .Set(x => x.IsDisabled, false)
                    .Set(x => x.IsDeleted, false)
                    .Where(x => input.Id.Contains(x.Id))
                    .ExecuteAffrowsAsync();
            }
        }

        /// <summary>
        ///  获取管理员基础信息
        /// </summary>
        /// <returns></returns>
        public async Task<ManagerBaseOutput1> GetBasicAsync()
        {
            if (!CurrentUser.Id.HasValue)
            {
                return new ManagerBaseOutput1();
            }

            //获取管理员信息
            var managerDto = await _managerRepository
                    .Where(x => x.Id == CurrentUser.Id)
                    .Include(x => x.Department)
                    .ToOneAsync(x => new ManagerBaseOutput1
                    {
                        DepartmentName = x.Department.Name
                    });

            if (managerDto == null)
                throw new UserFriendlyException("请先进行认证操作");

            //var managerIdAesKey = EncryptProvider.AESEncrypt(managerDto.Id.ToString(), _wechatMpOptions.AESKey);

            var bindUrl = $"{_wechatMpOptions.Domain}/ManagerBind?managerId={managerDto.Id}";

            var wechatQrCodeUrl = _mpUserAppService.GetAuthorizeUrl(bindUrl);

            managerDto.WechatQrCode = _qrCodeAppService.Get(wechatQrCodeUrl);

            return managerDto;
        }

        /// <summary>
        ///  获取管理员信息，用于表单编辑
        /// </summary>
        /// <returns></returns>
        public async Task<ManagerOutputDto> GetAsync(Guid id)
        {
            //获取管理员信息
            var managerDto = await _managerRepository
                .Where(x => x.Id == id)
                .Include(x => x.Department)
                .ToOneAsync(x => new ManagerOutputDto
                {
                    DepartmentName = x.Department.Name
                });

            if (managerDto == null)
                return new ManagerOutputDto();

            if (managerDto.DepartmentId.HasValue)
            {
                var departmentClassId = await _sysDepartmentRepository
                    .Where(x => x.Id == managerDto.DepartmentId)
                    .ToOneAsync(x => x.ClassId);

                if (!string.IsNullOrWhiteSpace(departmentClassId))
                {
                    var departmentClassIdList = departmentClassId
                        .Split(",")
                        .Select(x => x.ToLong())
                        .ToList();

                    var list = await _sysDepartmentRepository
                        .Where(x => departmentClassIdList.Contains(x.DepartId))
                        .ToListAsync(x => x.Id);

                    managerDto.DepartmentIdList = list.Skip(1).ToList();
                }
            }

            return managerDto;
        }

        /// <summary>
        ///  管理员保存
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task PutSaveAsync(ManagerSaveInputDto input)
        {
            if (input.DepartmentIdList == null || !input.DepartmentIdList.Any())
                throw new UserFriendlyException("请选择所属部门", "400");

            var model = ObjectMapper.Map<ManagerSaveInputDto, SysManager>(input);

            using var unitOfWork = _freeSql.CreateUnitOfWork();
            _managerRepository.UnitOfWork = unitOfWork;
            _sysManagerIdentity.UnitOfWork = unitOfWork;

            if (input.Id.HasValue)
            {
                model = await _managerRepository
                    .Where(x => x.Id == input.Id)
                    .FirstAsync();

                if (model == null)
                    throw new UserFriendlyException("当前员工不存在或被管理员删除");
            }

            //判断当前联系电话是否存在
            var exitsPhoneNumber = await _managerRepository
                .Where(x => x.Phone == input.Phone && x.Id != input.Id)
                .AnyAsync();

            if (exitsPhoneNumber)
                throw new UserFriendlyException("当前联系电话号码已存在");

            //判断用户名是否存在
            var exitsUserName = await _managerRepository
                .Where(x => x.UserName == input.UserName && x.Id != input.Id)
                .AnyAsync();

            if (exitsUserName)
                throw new UserFriendlyException("当前用户名已存在");

            //处理管理员角色编码和名字字段
            if (input.ManagerRoleList != null && input.ManagerRoleList.Any())
            {
                model.RoleId = input.ManagerRoleList
                    .Select(x => x.RoleId)
                    .GenericToString();
                model.RoleName = input.ManagerRoleList
                    .Select(x => x.RoleName)
                    .GenericToString();
            }

            //修改
            if (input.Id.HasValue)
            {
                ObjectMapper.Map(input, model);

                await _managerRepository.UpdateAsync(model);
            }
            else
            {
                model.Avatar = "/uploadFile/images/avatar8.jpg";
                await _managerRepository.InsertAsync(model);

                // 新增管理员密码
                var sysManagerIdentity = new SysManagerIdentity
                {
                    ManagerId = model.Id,
                    IdentityType = "Password",
                    Identifier = input.UserName,
                    Credential = "123@qwe".Encrypt32()
                };

                await _sysManagerIdentity.InsertAsync(sysManagerIdentity);
            }

            //更新管理员角色表
            if (input.ManagerRoleList != null && input.ManagerRoleList.Any())
            {
                model.ManagerRoles = input.ManagerRoleList.Select(x => new SysManagerRole
                {
                    RoleId = x.RoleId
                }).ToList();
            }

            if (model.ManagerRoles != null)
                await _managerRepository.SaveManyAsync(model, nameof(model.ManagerRoles));

            unitOfWork.Commit();
        }

        /// <summary>
        ///  管理员修改密码
        /// </summary>
        /// <param name="input">请求体</param>
        /// <returns></returns>
        public async Task UpdatePasswordAsync(ManagerPasswordSaveInputDto input)
        {
            var managerIdentity = await _freeSql
                .Select<SysManagerIdentity>()
                .Where(x => x.ManagerId == CurrentUser.Id && x.IdentityType == IdentityType.用户名密码)
                .ToOneAsync();

            managerIdentity.Credential = input.Password.Encrypt32();

            var result = await _freeSql.Update<SysManagerIdentity>()
                 .SetSource(managerIdentity)
                 .Where(x => x.ManagerId == CurrentUser.Id)
                 .ExecuteAffrowsAsync();

            _logger.LogInformation($"执行修改管理员密码：{result} 条");
        }

        /// <summary>
        ///  获取管理员选项列表
        /// </summary>
        /// <param name="name">管理员名字</param>
        /// <param name="departmentKey">部门编码</param>
        /// <returns></returns>
        public async Task<List<OptionOutputDto<Guid, string>>> GetOptionListAsync(string name, string departmentKey)
        {
            var departId = departmentKey.ToGuid();

            var list = await _freeSql.Select<SysManager>()
                .WhereIf(!string.IsNullOrWhiteSpace(name), x => x.Name.Contains(name))
                .WhereIf(!string.IsNullOrWhiteSpace(departmentKey), x => x.DepartmentId == departId)
                .Where(x => !x.IsDisabled && !x.IsDeleted)
                .ToListAsync(x => new OptionOutputDto<Guid, string>
                {
                    Label = x.Name,
                    Value = x.Id
                });

            return list;
        }

        /// <summary>
        ///  根据名称获取管理员选项列表
        /// </summary>
        /// <param name="name">管理员名字</param>
        /// <returns></returns>
        public async Task<List<ManagerOptionOutputDto>> GetOptionListByNameAsync(string name)
        {
            var list = await _freeSql.Select<SysManager>()
                .WhereIf(!string.IsNullOrWhiteSpace(name), x => x.Name.Contains(name))
                .Where(x=>x.CustomerId == null )
                .Include(x => x.Department)
                .ToListAsync(x => new ManagerOptionOutputDto
                {
                    Label = x.Name,
                    Value = x.Id,
                    DepartmentId = x.DepartmentId,
                    DepartmentName = x.Department.Name
                });

            return list;
        }

        /// <summary>
        ///  获取当前用户的部门信息
        /// </summary>
        /// <returns></returns>
        [RemoteService(IsEnabled = false)]
        public async Task<ManagerDepartmentOutputDto> GetManageDepartmentAsync()
        {
            var managerDepartment = await _managerRepository
                .Where(x => x.Id == CurrentUser.Id)
                .Include(x => x.Department)
                .ToOneAsync(x => new ManagerDepartmentOutputDto
                {
                    DepartmentId = x.DepartmentId,
                    DepartmentName = x.Department.Name
                });

            return managerDepartment;
        }
    }
}